# -*- coding: utf-8 -*-
import pygst
pygst.require("0.10")
import gst

from Observer import Observable
from Settings import Settings
from Exceptions.PlayerException import PlayerException
from MediaCollection.Database import Database

"""
Die Klasse dient dem Abspielen von Audio-Dateien. Hierbei wird momentan auf 
GStreamer gesetzt, das Programm ist somit also nicht plattformunabhängig. 
Eine cross-platform-Umstellung auf z. B. pyMedia wäre wünschenswert.

@author: Philipp Dermitzel
@date: 2011-03-30
@version: 1.1.3
"""
class AudioPlayer(Observable):
    
    """
    ----------------------------------------------------------------------------
    Klassenvariablen
    ----------------------------------------------------------------------------
    """

    """
    Die Klassen-Instanz der programmweiten Settings.
    @type: Settings
    """
    settings = Settings()
    
    """
    ----------------------------------------------------------------------------
    Konstruktor
    ----------------------------------------------------------------------------
    """
    
    """
    Der  Konstruktor. 
    @param path: Der Pfad zur abzuspielenden Datei 
    @type path: String
    @param track: Wahlweise kann auch eine Instanz der Track-Klasse übergeben 
        werden.
    @type track: Track
    """
    def __init__(self, path = None, track = None): 
        Observable.__init__(self)
        
        """
        Der Pfad zur abzuspielenden Datei. Wird vom Konstruktor gesetzt.
        @type: String
        """
        self.__pathToFile = None
    
        """
        Das Track-Objekt der gerade abgespielten Datei.
        @type: Track
        """
        self.__curTrack = None
    
        """
        Der Player, über den die (Audio-)Datei abgespielt wird.
        @type: unknown
        """
        self.__player = None
    
        """
        Gibt an, ob der Player gerade aktiv spielt oder nicht.
        @type: boolean
        """
        self.__curState = False
        
        if path is not None and track is None:
            self.setPathToFile(path)
        elif (path is None and track is not None):   
            self.setCurTrack(track)
        else: pass  
        
    """
    ----------------------------------------------------------------------------
    Getter und Setter
    ----------------------------------------------------------------------------
    """
    
    """
    Getter.
    @return: Der Pfad zur abzuspielenden Datei.
    @rtype: String
    """
    def getPathToFile(self):
        return self.__pathToFile
    
    """
    Getter.
    @return: Das Track-Objekt des gerade abgespielten Tracks.
    @rtype: Track
    """    
    def getCurTrack(self):
        return self.__curTrack
    
    """
    Getter.
    @return: Liefert den aktuellen Zustand des Players.
    @rtype: boolean
    """    
    def isPlaying(self):
        return self.__curState
    
    """
    Setzt den Pfad zur abzuspielenden Datei und erzeugt daraus das Track-Objekt.
    @param path: Der Pfad zur abzuspielenden Datei.
    @type path: String
    """    
    def setPathToFile(self, path):
        db = Database()
        db.select('*', 'Track', {'path':path})
        self.__curTrack = db.dbCursor.fetchone()
        self.__pathToFile = path
    
    """
    Setzt das Track-Objekt und speichert den Pfad zur abzuspielenden Datei.
    @param track: Das Track-Objekt.
    @type track: Track
    """    
    def setCurTrack(self, track):
        self.__curTrack = track
        self.__pathToFile = track.getPath()
        
    """
    ----------------------------------------------------------------------------
    Methoden der Klasse
    ----------------------------------------------------------------------------
    """
    
    """
    Spielt die angegebene Audio-File ab. Der Pfad zur Datei ist über 
    C(setPathToFile()) oder C(setCurTrack()) zu setzen. Wirft PlayerExceptions!
    @param stopOtherPlayer: Stoppt einen möglicherweise bereits laufenden Player
    @type stopOtherPlayer: boolean
    """
    def play(self, stopOtherPlayer = True):
        if self.__registerPlayer(stopOtherPlayer):
            try:
                self.__player = gst.element_factory_make("playbin2", "player")
                # Keine Video-Ausgabe, daher wird die
                # Ausgabe in den Fakesink geleitet.
                fakesink = gst.element_factory_make("fakesink", "fakesink")     
                self.__player.set_property("video-sink", fakesink)              
                bus = self.__player.get_bus()
                bus.add_signal_watch()
                bus.connect("message", self.__onMessage)
                self.__player.set_property("uri", "file://" + self.__pathToFile)
                self.__player.set_state(gst.STATE_PLAYING)
                self.__curState = True
            except: raise PlayerException("Konnte Datei nicht abspielen")
        else: raise PlayerException("""Es läuft bereits ein anderer Player, 
            der nicht unterbrochen werden kann""")
    
    """
    Stoppt die Wiedergabe der aktuell gespielten Audio-File. Erst hiernach kann 
    ein neuer Player gestartet werden.
    Falls nicht anders angegeben, geschiet dies automatisch (siehe C(play())
    """
    def stop(self):
        if self.__player is not None:
            self.__player.set_state(gst.STATE_NULL)
        del AudioPlayer.settings.getSettings()['curPlayer']
        self.__curState = False
        self.notifyObserver()
    
    """
    Registriert den aktuellen Player in den Settings, so dass dieser nicht von 
    anderen Playern unterbrochen werden kann.
    @param stopOtherPlayer: Stoppt einen möglicherweise bereits laufenden 
        Player. Dieser Parameter sollte über C(play()) gesetzt werden.
    @type stopOtherPlayer: boolean
    @return: False falls bereits ein Player läuft, sonst True.
    @rtype: boolean
    """    
    def __registerPlayer(self, stopOtherPlayer):    
        try: 
            rPlayer = AudioPlayer.settings.getSetting('curPlayer')
            if stopOtherPlayer:
                rPlayer.stop()
                AudioPlayer.settings.setSetting('curPlayer', self)
                return True
            else: return False
        except: 
            AudioPlayer.settings.setSetting('curPlayer', self)
            return True
    
    """
    Reagiert auf Messages des Players.
    Methode von: http://pygstdocs.berlios.de/pygst-tutorial/playbin.html
    """        
    def __onMessage(self, bus, message):
        t = message.type
        if t == gst.MESSAGE_EOS:
            self.__player.set_state(gst.STATE_NULL)
            self.__curState = False
            self.notifyObserver()
        elif t == gst.MESSAGE_ERROR:
            self.__player.set_state(gst.STATE_NULL)
            self.__curState = False
            self.notifyObserver()
            raise PlayerException("Fehler beim Abpsielen")
